#include <glf.h>
#include <glfwindow.h>
#include <glftime.h>
#include <glfevent.h>

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#if defined( GLF_PLATFORM_WIN32 )
static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;
static PFNWGLCREATECONTEXTATTRIBSARBPROC    wglCreateContextAttribsARB;
static PFNWGLCHOOSEPIXELFORMATARBPROC       wglChoosePixelFormatARB;
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
static PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
//static PFNWGLMAKECONTEXTCURRENTARBPROC      wglMakeContextCurrentARB;
//static PFNWGLGETEXTENSIONSSTRINGARBPROC     wglGetExtensionStringARB;
//static PFNWGLGETPIXELFORMATATTRIBIVARBPROC  wglGetPixelFormatAttribivARB;
//static PFNWGLGETPIXELFORMATATTRIBFVARBPROC  wglGetPixelFormatAttribfvARB;
#else
static PFNGLXCREATECONTEXTATTRIBSARBPROC    glXCreateContextAttribsARB;
#endif

static glfWindowInfo* g_wininfo;
static int g_rcattrib[32];
static int g_fbattrib[32];
static int g_configResetOnce = 0;
static int g_fullscreen = 0;
static int g_sizable = 0;
static int g_cursorflags = 0;
static int g_disablevsync = 0;

#if defined( GLF_PLATFORM_WIN32 )

#define CLASS_NAME "OpenGL 3.X"

static setPath()
{
	FILE *f;
	char path[MAX_PATH];
	f=fopen("glf_path.txt","rt");
	if( f )
	{
		fgets( path, MAX_PATH, f );
		fclose( f );
		SetCurrentDirectory( path );
	}
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	int x,y;
	int ox,oy;
	RECT rect;
	glfGetWindowPosition( &x, &y );
	glfGetDisplayOffset( &ox, &oy );
	x+=ox;
	y+=oy;
	rect.left = x;
	rect.top = y;
	rect.bottom = y+glfGetHeight();
	rect.right = x+glfGetWidth();
	switch (uMsg)									// Check For Windows Messages
	{
	case WM_ACTIVATE:							// Watch For Window Activate Message
		{
			if( LOWORD(wParam) == WA_INACTIVE )
			{
				g_wininfo->active=0;
				//printf( "Inactive\n" );
				ReleaseCapture();

			}
			else
			{
				g_wininfo->active=1;
				//printf( "Active\n" );
				glfResetTime();
				if(g_cursorflags && 2)
				{
				SetCapture( hWnd );
				SetCursor( LoadCursor(NULL, IDC_ARROW) );
				}
			}
			return 0;								// Return To The Message Loop
		}
		break;
	case WM_MOUSEMOVE:
		if( g_wininfo->active & (g_cursorflags && 2) )
		{
			ClipCursor( &rect );
		}
		return 0;
	//case WM_SETCURSOR:
	//	return 0;

	/*case WM_WINDOWPOSCHANGING:
		g_wininfo->active=1;
		glfResetTime();
		printf( "changing\n" );
		return 0;
	case WM_WINDOWPOSCHANGED:
		g_wininfo->active=0;
		printf( "changed\n" );
		return 0;*/

	
	case WM_SYSCOMMAND:							// Intercept System Commands
		{
			switch (wParam)							// Check System Calls
			{
			case SC_SCREENSAVE:					// Screensaver Trying To Start?
			case SC_MONITORPOWER:				// Monitor Trying To Enter Powersave?
				return 0;							// Prevent From Happening
			}
			break;									// Exit
		}

	//case WM_DESTROY:
	case WM_CLOSE:								// Did We Receive A Close Message?
		{
			PostQuitMessage(0);						// Send A Quit Message
			return 0;								// Jump Back
		}

	case WM_KEYDOWN:							// Is A Key Being Held Down?
		{
			//if (!m_lesson->KeyDown(wParam))
			//    PostQuitMessage(0);
			//	m_keys[ wParam ] = 1;
			return 0;								// Jump Back
		}

	case WM_KEYUP:								// Has A Key Been Released?
		{
			//if (!m_lesson->KeyUp(wParam))
			//   PostQuitMessage(0);
			if( wParam == 27 )
			{
				PostQuitMessage(0);
			}
			//m_keys[ wParam ] = 0;
			return 0;								// Jump Back
		}

	case WM_SIZE:								// Resize The OpenGL Window
		{
			g_wininfo->rect.right = LOWORD( lParam );
			g_wininfo->rect.bottom = HIWORD( lParam );
			g_wininfo->callbackResize( g_wininfo->dataResize );
			return 0;								// Jump Back
		}
	}

	// Pass All Unhandled Messages To DefWindowProc
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

static void windowsInitialise()
{
	WNDCLASS wc;

	g_wininfo->hinstance	= GetModuleHandle(NULL);

	memset( &wc, 0, sizeof( wc ) );
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc	= (WNDPROC) WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= g_wininfo->hinstance;
	wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= NULL;
	wc.lpszMenuName	= NULL;
	wc.lpszClassName	= CLASS_NAME;
	RegisterClass(&wc);	//Andy - Todo: Error handle, returns 0 on error

}

static void windowsTerminate()
{
	UnregisterClass( CLASS_NAME, g_wininfo->hinstance );	//Andy - Todo: Error handle, returns 0 on error		
	g_wininfo->hinstance = 0;
}

static int windowsCreateWindow( const char *title, const int width, const int height )
{
	HWND tempWnd;
	HDC	 tempDC;
	static const	PIXELFORMATDESCRIPTOR pfd=				// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,											// Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		24,											// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		0,											// No Alpha Buffer
		0,											// Shift Bit Ignored
		0,											// No Accumulation Buffer
		0, 0, 0, 0,									// Accumulation Bits Ignored
		0,											// 16Bit Z-Buffer (Depth Buffer)
		0,											// No Stencil Buffer
		0,											// No Auxiliary Buffer
		PFD_MAIN_PLANE,								// Main Drawing Layer
		0,											// Reserved
		0, 0, 0										// Layer Masks Ignored
	};

	GLuint		PixelFormat;
	DWORD		dwExStyle;
	DWORD		dwStyle;
	RECT		WindowRect;
	int pixelFormat;
	float fAttributes[]={0,0};
	HGLRC tempContext;
	UINT numFormats;
	RECT DesktopRect;
	int x=0;
	int y=0;
	DEVMODE dmScreenSettings;
	if( g_fullscreen )	// Attempt Fullscreen Mode?
	{
		// Device Mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	// Makes Sure Memory's Cleared
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);		// Size Of The Devmode Structure
		dmScreenSettings.dmPelsWidth	= width;				// Selected Screen Width
		dmScreenSettings.dmPelsHeight	= height;				// Selected Screen Height
		dmScreenSettings.dmBitsPerPel	= 32;					// Selected Bits Per Pixel
		dmScreenSettings.dmFields= DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
		// Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			g_fullscreen = 0;
		}
	}

	tempWnd = GetDesktopWindow();
	GetWindowRect( tempWnd, &DesktopRect ); 
	
	windowsInitialise();

	WindowRect.left=(long)0;
	WindowRect.right=(long)width;
	WindowRect.top=(long)0;
	WindowRect.bottom=(long)height;

	if (g_fullscreen)											// Are We Still In Fullscreen Mode?
	{
		dwExStyle=WS_EX_APPWINDOW;								// Window Extended Style
		dwStyle=WS_POPUP;										// Windows Style
		//ShowCursor(false);										// Hide Mouse Pointer
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;			// Window Extended Style
		if( g_sizable )
		{
			dwStyle=WS_OVERLAPPEDWINDOW;							// Windows Style
		}
		else
		{
			dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
		}
		
	}


	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);		// Adjust Window To true Requested Size


	
	// Create The Window
	if (!(g_wininfo->hwnd=CreateWindowEx(	dwExStyle,							// Extended Style For The Window
		CLASS_NAME,							// Class Name
		title,								// Window Title
		dwStyle |							// Defined Window Style
		WS_CLIPSIBLINGS |					// Required Window Style
		WS_CLIPCHILDREN,					// Required Window Style
		0, 0,								// Window Position
		(WindowRect.right-WindowRect.left),	// Calculate Window Width
		(WindowRect.bottom-WindowRect.top),	// Calculate Window Height
		NULL,								// No Parent Window
		NULL,								// No Menu
		g_wininfo->hinstance,				// Instance
		0)))								// Pass a pointer to this window so we can use a member WndProc
	{
		return 0;
	}

	if (!(g_wininfo->hdc=GetDC(g_wininfo->hwnd)))
	{
		return 0;
	}

	if (!(PixelFormat=ChoosePixelFormat(g_wininfo->hdc,&pfd)))
	{
		return 0;
	}

	if (!SetPixelFormat(g_wininfo->hdc,PixelFormat,&pfd))
	{
		return 0;
	}

	tempContext = wglCreateContext(g_wininfo->hdc);
	wglMakeCurrent(g_wininfo->hdc,tempContext);

	tempWnd = g_wininfo->hwnd;
	tempDC = g_wininfo->hdc;

	wglGetExtensionsStringARB =
		(PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");

#ifdef _DEBUG
	printf( "wgl Extensions (ARB): %s\n", wglGetExtensionsStringARB( g_wininfo->hdc ) );
#endif
	// Get Our Pixel Format
	wglChoosePixelFormatARB =
		(PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");


	wglChoosePixelFormatARB(g_wininfo->hdc,g_fbattrib,fAttributes,1,&pixelFormat,&numFormats);

	x = ((DesktopRect.right-DesktopRect.left)/2)-((WindowRect.right-WindowRect.left)/2);
	y = ((DesktopRect.bottom-DesktopRect.top)/2)-((WindowRect.bottom-WindowRect.top)/2);

	// Create The Window
	if (!(g_wininfo->hwnd=CreateWindowEx(	dwExStyle,							// Extended Style For The Window
		CLASS_NAME,						// Class Name
		title,								// Window Title
		dwStyle |							// Defined Window Style
		WS_CLIPSIBLINGS |					// Required Window Style
		WS_CLIPCHILDREN,					// Required Window Style
		x, y,								// Window Position
		WindowRect.right-WindowRect.left,	// Calculate Window Width
		WindowRect.bottom-WindowRect.top,	// Calculate Window Height
		NULL,								// No Parent Window
		NULL,								// No Menu
		g_wininfo->hinstance,				// Instance
		0)))								// Pass a pointer to this window so we can use a member WndProc
	{
		//Destroy();								// Reset The Display
		//MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

	if (!(g_wininfo->hdc=GetDC(g_wininfo->hwnd)))							// Did We Get A Device Context?
	{
		//Destroy();								// Reset The Display
		//MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

	if (!SetPixelFormat(g_wininfo->hdc,pixelFormat,&pfd))		// Are We Able To Set The Pixel Format?
	{
		//Destroy();								// Reset The Display
		//MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;								// Return false
	}

	wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");
	if (wglCreateContextAttribsARB == NULL) //OpenGL 3.0 is not supported
	{
		//MessageBox(NULL,"Cannot get Proc Adress for CreateContextAttribs", "ERROR",MB_OK);
		//Destroy();
		wglDeleteContext(tempContext);
		return 0;
	}

	if (!(g_wininfo->hrc=wglCreateContextAttribsARB(g_wininfo->hdc,0, g_rcattrib)))
	{
		wglDeleteContext(tempContext);
		//Destroy();								// Reset The Display
		//MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;								// Return false
	}

	wglMakeCurrent(0,0);
	wglDeleteContext(tempContext);
	ReleaseDC( tempWnd, tempDC );
	DestroyWindow( tempWnd );

	if (!wglMakeCurrent(g_wininfo->hdc,g_wininfo->hrc))					// Try To Activate The Rendering Context
	{
		//Destroy();								// Reset The Display
		//MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;								// Return false
	}

	ShowWindow(g_wininfo->hwnd,SW_SHOW);						// Show The Window
	SetForegroundWindow(g_wininfo->hwnd);						// Slightly Higher Priority
	SetFocus(g_wininfo->hwnd);									// Sets Keyboard Focus To The Window

	wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
	wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT");

	if( g_disablevsync )
	{
		wglSwapIntervalEXT( 0 );
	}
	else
	{
		wglSwapIntervalEXT( 1 );
	}
	GLF_TRACE( "wglGetSwapIntervalEXT returned %d", wglGetSwapIntervalEXT() );
	return 1;
}

#endif
    
int glfCreateDummyContext(void)
{
    glfWindowInfo info;
    
    #if defined( GLF_PLATFORM_WIN32 )
        /**********************************************************
        *******************Windows setup **************************
        **********************************************************/
        /* Add later */
        memset(&info, 0, sizeof(glfWindowInfo));
    #else
    
        /**********************************************************
        ********************** **X setup **************************
        **********************************************************/
        #if 0
        static const int fb_attributes[] = {
            GLX_DOUBLEBUFFER, True,
            GLX_RENDER_TYPE, GLX_RGBA_BIT,
            GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
            GLX_X_RENDERABLE, True,
            GLX_RED_SIZE, 8, 
            GLX_GREEN_SIZE, 8, 
            GLX_BLUE_SIZE, 8,
            GLX_ALPHA_SIZE, 8,
            GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
            None
        };
    
        Colormap colormap;
        /*XWindowAttributes winattrib; */
        GLXFBConfig* fbconfig;
        XVisualInfo* visualinfo;
        int num, screen;
        XSetWindowAttributes winsetattrib;

        memset(&info, 0, sizeof(glfWindowInfo));
        info.display = XOpenDisplay(NULL);
        screen = DefaultScreen(info.display);
        fbconfig = glXChooseFBConfig(info.display, screen, fb_attributes, &num);
        if(!fbconfig || !num){
            fprintf(stderr, "Failed to get a proper fbconfig\n");
            return 0;
        }
        /* VisualInfo structure needed for the palette */
        visualinfo = glXGetVisualFromFBConfig(info.display, fbconfig[0]);
        if(!visualinfo){
            fprintf(stderr,"Failed to get a proper visualinfo pointer.\n");
            return 0;
        }
        
        if( (info.rc = glXCreateNewContext(info.display, fbconfig[0], GLX_RGBA_TYPE, NULL, True)) == NULL){
            fprintf(stderr, "Failed to create dummy context.\n");
            return 0;
        }
        
        memset(&winsetattrib, 0, sizeof(XSetWindowAttributes));
        colormap = XCreateColormap(info.display, RootWindow(info.display, visualinfo->screen), visualinfo->visual, AllocNone);
        winsetattrib.colormap = colormap;
        winsetattrib.border_pixel = 0;

        info.window = XCreateWindow(info.display, RootWindow(info.display, visualinfo->screen), 
            0, 0, 32, 32, 0, visualinfo->depth, InputOutput, visualinfo->visual, CWBorderPixel | CWColormap, &winsetattrib);

        //XSetStandardProperties(info.display, info.window, title, title, None, NULL, 0, NULL);
        //XMapRaised(info.display, info.window);
        glXMakeContextCurrent(info.display, info.window, info.window, info.rc);
        
        /* Phew.. now we can get the awful function pointers
            NOTE: IHVs actually exposes all OpenGL functions in the libGL.so on X, no matter whether
            they are GL/GLX extensions or not. So if you're lazy, you can #define GL3_PROTOTYPES to link statically.
            If someone tries to run your program with old drivers or with an inferior graphics card,
            /lib/ld-linux.so.2 (the dynamic linker) will complain about missing symbols. So don't do
            this in versions of your software you plan to ship/release. */
        glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress("glXCreateContextAttribsARB");
        
        /*  We got the function pointers we wanted. The rest of the code is cleanup-code.
            TODO: Figure out whether or not it is guaranteed that pointers from glX/wgl GetProcAddress
            is persistent after the context is destroyed. */
        glXMakeContextCurrent(info.display, None, None, NULL);
        glXDestroyContext(info.display, info.rc);
        XDestroyWindow(info.display, info.window);
        XCloseDisplay(info.display);
    #endif
    
    void* handle = dlopen("libGL.so", RTLD_NOW | RTLD_GLOBAL);
    if(!handle){
        fprintf(stderr, "Unable to open libGL.so. dlerror returned: %s\n",dlerror());
        return 0;
    }
    if( !(glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)dlsym(handle, "glXCreateContextAttribsARB")) ){
        fprintf(stderr, "Unable to get glXCreateContextAttribsARB with dlsym. dlerror returned: %s\n",dlerror());
        dlclose(handle);
        return 0;
    }
    dlclose(handle);
    #endif
    return 1;
}

static void ResetAttribs(void)
{

	g_fullscreen = 0;
	g_sizable = 0;
	g_disablevsync = 0;

    #if defined( GLF_PLATFORM_WIN32 )

	g_fbattrib[0 ] = WGL_DOUBLE_BUFFER_ARB;		g_fbattrib[1 ] = TRUE;
	g_fbattrib[2 ] = WGL_RED_BITS_ARB;			g_fbattrib[3 ] = 8;
	g_fbattrib[4 ] = WGL_GREEN_BITS_ARB;		g_fbattrib[5 ] = 8;
	g_fbattrib[6 ] = WGL_BLUE_BITS_ARB;			g_fbattrib[7 ] = 8;
	g_fbattrib[8 ] = WGL_ALPHA_BITS_ARB;		g_fbattrib[9 ] = 8;
	g_fbattrib[10] = WGL_DEPTH_BITS_ARB;		g_fbattrib[11] = 24;
	g_fbattrib[12] = WGL_STENCIL_BITS_ARB;		g_fbattrib[13] = 8;
	g_fbattrib[14] = WGL_SAMPLE_BUFFERS_ARB;	g_fbattrib[15] = 1;
	g_fbattrib[16] = WGL_SAMPLES_ARB;			g_fbattrib[17] = 1;
	g_fbattrib[18] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT;	g_fbattrib[19] = FALSE;
	g_fbattrib[20] = WGL_DRAW_TO_WINDOW_ARB;	g_fbattrib[21] = TRUE;
	g_fbattrib[22] = WGL_SUPPORT_OPENGL_ARB;	g_fbattrib[23] = TRUE;
	g_fbattrib[24] = WGL_COLOR_BITS_ARB;		g_fbattrib[25] = 24;
	g_fbattrib[26] = WGL_ACCELERATION_ARB;		g_fbattrib[27] = WGL_FULL_ACCELERATION_ARB;
	g_fbattrib[28] = 0;							g_fbattrib[29] = 0;

	g_rcattrib[0] = WGL_CONTEXT_MAJOR_VERSION_ARB;  g_rcattrib[1] = 3;
	g_rcattrib[2] = WGL_CONTEXT_MINOR_VERSION_ARB;  g_rcattrib[3] = 1;
	g_rcattrib[4] = WGL_CONTEXT_FLAGS_ARB;          g_rcattrib[5] = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
	g_rcattrib[6] = 0;								g_rcattrib[7] = 0;

    #else
        g_fbattrib[0 ] = GLX_DOUBLEBUFFER;  g_fbattrib[1 ] = True;
        g_fbattrib[2 ] = GLX_RED_SIZE;      g_fbattrib[3 ] = 8;
        g_fbattrib[4 ] = GLX_GREEN_SIZE;    g_fbattrib[5 ] = 8;
        g_fbattrib[6 ] = GLX_BLUE_SIZE;     g_fbattrib[7 ] = 8;
        g_fbattrib[8 ] = GLX_ALPHA_SIZE;    g_fbattrib[9 ] = 8;
        g_fbattrib[10] = GLX_DEPTH_SIZE;    g_fbattrib[11] = 24;
        g_fbattrib[10] = GLX_STENCIL_SIZE;  g_fbattrib[11] = 8;
        g_fbattrib[12] = GLX_RENDER_TYPE;   g_fbattrib[13] = GLX_RGBA_BIT;
        g_fbattrib[14] = GLX_DRAWABLE_TYPE; g_fbattrib[15] = GLX_WINDOW_BIT;
        g_fbattrib[16] = GLX_X_RENDERABLE;  g_fbattrib[17] = True;
        g_fbattrib[18] = GLX_X_VISUAL_TYPE; g_fbattrib[19] = GLX_TRUE_COLOR;
        g_fbattrib[20] = GLX_SAMPLE_BUFFERS;g_fbattrib[21] = True;
        g_fbattrib[22] = GLX_SAMPLES;       g_fbattrib[23] = 1;
        g_fbattrib[24] = None;              g_fbattrib[25] = None;
        
        g_rcattrib[0] = GLX_CONTEXT_MAJOR_VERSION_ARB;  g_rcattrib[1] = 3;
        g_rcattrib[2] = GLX_CONTEXT_MINOR_VERSION_ARB;  g_rcattrib[3] = 1;
        g_rcattrib[4] = GLX_RENDER_TYPE;                g_rcattrib[5] = GLX_RGBA_TYPE;
        g_rcattrib[6] = GLX_CONTEXT_FLAGS_ARB;          g_rcattrib[7] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
        g_rcattrib[8] = None;                           g_rcattrib[9] = None;
    #endif    
}

void glfConfigSetAttribute(int name, int attrib)
{
    if(!g_configResetOnce){
        ResetAttribs();
        g_configResetOnce = 1;
    }

	//Common attributes
	switch(name)
	{
	case glfSizable:
		g_sizable = attrib;
		break;
	case glfFullscreen:
		g_fullscreen = attrib;
		break;
	case glfDisableVsync:
		g_disablevsync = attrib;
		break;
	}
    
	//Platform attributes
    #if defined( GLF_PLATFORM_WIN32 )
		switch(name)
		{
		case glfFramebufferType:
			/*if(attrib & glfFloat){
				g_fbattrib[13] = GLX_RGBA_FLOAT_BIT_ARB | GLX_RGBA_FLOAT_BIT_ARB;
				g_rcattrib[5] = GLX_RGBA_FLOAT_TYPE_ARB;
			}
			if(attrib & glfRGBA)
				g_fbattrib[13] &= ~ WGL_TYPE_RGBA_FLOAT_ARB;*/
			if(attrib & glfSRGB)
				g_fbattrib[19] = TRUE;
			break;
		case glfMultiSampleLevel:
			g_fbattrib[17] = attrib;
			break;
		case glfMajorVersion:
			g_rcattrib[1] = attrib;
			break;
		case glfMinorVersion:
			g_rcattrib[3] = attrib;
			break;
		case glfContextFlags:
			if(attrib & glfContextForwardCompatible)
				g_rcattrib[7] |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
			else if(attrib & glfContextDebug)
				g_rcattrib[7] |= WGL_CONTEXT_DEBUG_BIT_ARB;
			break;
		}
    #else
        switch(name)
        {
            case glfFramebufferType:
                if(attrib & glfFloat){
                    g_fbattrib[13] = GLX_RGBA_FLOAT_BIT_ARB | GLX_RGBA_FLOAT_BIT_ARB;
                    g_rcattrib[5] = GLX_RGBA_FLOAT_TYPE_ARB;
                }
                if(attrib & glfRGBA)
                    g_fbattrib[13] &= ~GLX_RGBA_FLOAT_BIT_ARB;
                if(attrib & glfSRGB)
                    g_fbattrib[24] = GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT;
            break;
            case glfMultiSampleLevel:
                g_fbattrib[23] = attrib;
            break;
            case glfMajorVersion:
                g_rcattrib[1] = attrib;
            break;
            case glfMinorVersion:
                g_rcattrib[3] = attrib;
            break;
            case glfContextFlags:
                if(attrib & glfContextForwardCompatible)
                    g_rcattrib[7] |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
                else if(attrib & glfContextDebug)
                    g_rcattrib[7] |= GLX_CONTEXT_DEBUG_BIT_ARB;
            break;
        }
    #endif
    return;
}

void debugInteger( const char *name, const GLenum value )
{
	int v = 0;
	glGetIntegerv( value, &v );
	GLF_TRACE( "%s: %u", name, v );
}

void debugString( const char *name, const GLenum value )
{
	const char *c = glGetString( value );
	GLF_TRACE( "%s: %s", name, c );
}

void debugBoolean( const char *name, const GLenum value )
{
	GLboolean b = 0;
	glGetBooleanv( value, &b );
	GLF_TRACE( "%s: %s", name, b ? "TRUE" : "FALSE" );
}

int glfCreateWindow(const char* title, int width, int height)
{
    #if defined( GLF_PLATFORM_WIN32 )
    #else
        Colormap colormap;
        XWindowAttributes winattrib;
        GLXFBConfig* fbconfig;
        XVisualInfo* visual;
        int num, screen;
        XSetWindowAttributes winsetattrib;
        Atom wmDelete;
    #endif
    
    if(!g_configResetOnce){
        ResetAttribs();
        g_configResetOnce = 1;
    }
    
    if(g_wininfo){
        fprintf(stderr, "Destroy the old Window before creating a new one.\n");
        return 0;
    }
    g_wininfo = malloc( sizeof(glfWindowInfo) );
    if(!g_wininfo){
        fprintf(stderr, "Allocation failure.\n");
        return 0;
    }
    memset(g_wininfo, 0, sizeof(glfWindowInfo));
    
    glfCreateDummyContext();
    
    #if defined( GLF_PLATFORM_WIN32 )

		SetConsoleTitle( title );

		windowsCreateWindow( title, width, height );

		g_wininfo->rect.left = 0;
		g_wininfo->rect.top = 0;
		g_wininfo->rect.right = width;
		g_wininfo->rect.bottom = height;



    #else
        g_wininfo->display = XOpenDisplay(NULL);
        screen = DefaultScreen(g_wininfo->display);

        fbconfig = glXChooseFBConfig(g_wininfo->display, screen, g_fbattrib, &num);
        if(!fbconfig || !num){
            fprintf(stderr,"Failed to get a proper fbconfig\n");
            return 0;
        }

        visual = glXGetVisualFromFBConfig(g_wininfo->display, fbconfig[0]);
        if(!visual){
            fprintf(stderr,"Failed to get a proper visual\n");
            return 0;
        }

        if(!(void*)glXCreateContextAttribsARB)
        {
            fprintf(stderr, "Failed to load glXCreateContextAttribsARB. OpenGL3.x is not supported.\n");
            return 0;
        }
        
        if( (g_wininfo->rc = glXCreateContextAttribsARB(g_wininfo->display, fbconfig[0], 0, GL_TRUE, g_rcattrib)) == NULL){
            fprintf(stderr,"Failed to create context\n");
            return 0;
        }

        memset(&winsetattrib, 0, sizeof(winsetattrib));
        colormap = XCreateColormap(g_wininfo->display, RootWindow(g_wininfo->display, visual->screen), visual->visual, AllocNone);
        winsetattrib.colormap = colormap;
        winsetattrib.border_pixel = 0;
        winsetattrib.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;

        g_wininfo->window = XCreateWindow(g_wininfo->display, RootWindow(g_wininfo->display, visual->screen), 
            0, 0, width, height, 0, visual->depth, InputOutput, visual->visual, CWBorderPixel | CWColormap | CWEventMask, &winsetattrib);

        wmDelete = XInternAtom(g_wininfo->display, "WM_DELETE_WINDOW", True);
        XSetWMProtocols(g_wininfo->display, g_wininfo->window, &wmDelete, 1);
        XSetStandardProperties(g_wininfo->display, g_wininfo->window, title, title, None, NULL, 0, NULL);
        XMapRaised(g_wininfo->display, g_wininfo->window);

        if( (glXMakeContextCurrent(g_wininfo->display, g_wininfo->window, g_wininfo->window, g_wininfo->rc)) == False){
            fprintf(stderr, "Unable to bind OpenGL Context.\n");
            return 0;
        }

        XGetWindowAttributes(g_wininfo->display, g_wininfo->window, &winattrib);
        
        g_wininfo->rect.left = winattrib.x;
        g_wininfo->rect.top = winattrib.y;
        g_wininfo->rect.right = winattrib.width;
        g_wininfo->rect.bottom = winattrib.height;

    #endif
	InitGLFunctions();


	//Query GL


#ifdef _DEBUG

	debugInteger( "GL_MAJOR_VERSION", GL_MAJOR_VERSION );
	debugInteger( "GL_MINOR_VERSION", GL_MINOR_VERSION );
	debugString( "GL_RENDERER", GL_RENDERER );
	debugString( "GL_SHADING_LANGUAGE_VERSION", GL_SHADING_LANGUAGE_VERSION );
	debugString( "GL_VENDOR", GL_VENDOR );
	debugString( "GL_VERSION", GL_VERSION );

	debugBoolean( "GL_DOUBLEBUFFER", GL_DOUBLEBUFFER );
	debugBoolean( "GL_STEREO", GL_STEREO );
	debugInteger( "GL_SAMPLEBUFFERS", GL_SAMPLE_BUFFERS );
	debugInteger( "GL_SAMPLES", GL_SAMPLES );

#endif

    return 1;
}

int glfGetWidth()
{
    return g_wininfo->rect.right;
}

int glfGetHeight()
{
    return g_wininfo->rect.bottom;
}

int glfGetWindowPosition( int *x, int *y )
{
#if defined( GLF_PLATFORM_WIN32 )
	WINDOWINFO pwi;
	if( g_wininfo )
	{
		GetWindowInfo( g_wininfo->hwnd, &pwi );
		*x = pwi.rcWindow.left;
		*y = pwi.rcWindow.top;
		return 1;
	}
#endif
	return 0;
}

void glfSetCursorFlags( int flags )
{
	if( ( g_cursorflags & 1 ) != ( flags & 1 ) )
	{
		if( flags & 1 )
		{
			ShowCursor( (flags & 1) == 0 );
		}
	}
	g_cursorflags = flags;
}

int glfGetDisplayOffset( int *x, int *y )
{
#if defined( GLF_PLATFORM_WIN32 )
	if( g_wininfo )
	{
		*y = GetSystemMetrics( SM_CYCAPTION );
		if( !g_sizable )
		{
			*x = GetSystemMetrics( SM_CXFIXEDFRAME );
			*y += GetSystemMetrics( SM_CYFIXEDFRAME );
		}
		else
		{
			*x = GetSystemMetrics( SM_CXSIZEFRAME );
			*y += GetSystemMetrics( SM_CYSIZEFRAME );
		}
		return 1;
	}
#endif
	return 0;
}

void glfDestroyWindow(void)
{
    #if defined( GLF_PLATFORM_WIN32 )
		wglMakeCurrent( 0, 0 );
		wglDeleteContext( g_wininfo->hrc );
		ReleaseDC( g_wininfo->hwnd, g_wininfo->hdc );
		DestroyWindow( g_wininfo->hwnd );
		windowsTerminate();
		if( g_fullscreen )
		{
			ChangeDisplaySettings( 0, 0 );
		}
    #else
        glXMakeContextCurrent(g_wininfo->display, None, None, NULL);
        glXDestroyContext(g_wininfo->display, g_wininfo->rc);
        XDestroyWindow(g_wininfo->display, g_wininfo->window);
        XCloseDisplay(g_wininfo->display);
    #endif
	ResetAttribs();
    free(g_wininfo);
    g_wininfo = NULL;
}

int glfSetCallBack(int type, glfCallback callback, void* userdata)
{
    if(!g_wininfo){
        fprintf(stderr, "Call glfCreateWindow first.\n");
        return 0;
    }
    switch(type)
    {
	case glfCallbackInitialise:
		g_wininfo->callbackInitialise = callback;
		g_wininfo->dataInitialise = userdata;
		break;
        case glfCallbackThink:
            g_wininfo->callbackThink = callback;
            g_wininfo->dataThink = userdata;
        break;
        case glfCallbackRender:
            g_wininfo->callbackRender = callback;
            g_wininfo->dataRender = userdata;
        break;
        case glfCallbackResize:
            g_wininfo->callbackResize = callback;
            g_wininfo->dataResize = userdata;
        break;
		case glfCallbackTerminate:
			g_wininfo->callbackTerminate = callback;
			g_wininfo->dataTerminate = userdata;
			break;
    }
    return 1;
}

int glfMainloop(void)
{
	int loop = 1;
    #if defined( GLF_PLATFORM_WIN32 )
	MSG msg;
	int x,y;
	int ox,oy;
	RECT rect;
	glfGetWindowPosition( &x, &y );
	glfGetDisplayOffset( &ox, &oy );
	x+=ox;
	y+=oy;
	rect.left = x;
	rect.top = y;
	rect.bottom = y+glfGetHeight();
	rect.right = x+glfGetWidth();
    #else
        XEvent event;
    #endif

    if(!g_wininfo){
        fprintf(stderr, "Call glfCreateWindow first.\n");
        return 0;
    }
    if(!(g_wininfo->callbackThink && g_wininfo->callbackRender && g_wininfo->callbackResize)){
        fprintf(stderr, "Setup callbacks first.\n");
        return 0;
    }

#if defined( GLF_PLATFORM_WIN32 )

	setPath();

#endif

	if( g_wininfo->callbackInitialise )
	{
		g_wininfo->callbackInitialise( g_wininfo->dataInitialise );
	}
    
    #if defined( GLF_PLATFORM_WIN32 )


	glfResetTime();
	while(loop)
	{

		if( (g_cursorflags && 2) && g_wininfo->active )
		{
			ClipCursor( &rect );
		}
		while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )	//As long as messages are available
		{
			if (msg.message==WM_QUIT)				//Quit message?
			{
				loop = 0;
				g_wininfo->active = 0;
			}
			else									//other messages
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		if( g_wininfo->active )	//Only update/render when the window is active
		{
			glfDoEvents();
			g_wininfo->callbackThink(g_wininfo->dataThink);
			g_wininfo->callbackRender(g_wininfo->dataRender);
			SwapBuffers(g_wininfo->hdc);
		}
	}

    #else
        while(loop)
        {
            while(XPending(g_wininfo->display) > 0)
            {
                XNextEvent(g_wininfo->display, &event);
                switch(event.type)
                {
                    case Expose:
                        g_wininfo->callbackRender(g_wininfo->dataRender);
                        glXSwapBuffers(g_wininfo->display, g_wininfo->window);
         	        break;
                    case ConfigureNotify:
                        g_wininfo->rect.right = event.xconfigure.width;
                        g_wininfo->rect.bottom = event.xconfigure.height;
                        g_wininfo->callbackResize(g_wininfo->dataResize);
	                break;
                
                    case ButtonPress:
                    break;
                
                    case KeyPress:
                    if (XLookupKeysym(&event.xkey, 0) == XK_Escape)
                        loop = False;
                    break;
                    case ClientMessage:
                        if (*XGetAtomName(g_wininfo->display, event.xclient.message_type) == *"WM_PROTOCOLS")
                            loop = False;
                        break;
                    default:
                        break;
                }
            }
			glfDoEvents();
            g_wininfo->callbackThink(g_wininfo->dataThink);
            g_wininfo->callbackRender(g_wininfo->dataRender);
            glXSwapBuffers(g_wininfo->display, g_wininfo->window);
        }
    #endif

		if( g_wininfo->callbackTerminate )
		{
			g_wininfo->callbackTerminate( g_wininfo->dataTerminate );
		}
    return 1;
}

